package zdream.rockchronicle.core.graphics;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import zdream.control.assets.FoeDescription;
import zdream.control.world.MHookType;
import zdream.control.world.MHooks;
import zdream.control.world.buff.AttackModule;
import zdream.control.world.buff.HealthModule;
import zdream.control.world.MFoe;
import zdream.control.world.MFoeBuff;
import zdream.rockchronicle.RockChronicle;

import static zdream.control.world.MFoeConstants.*;

/**
 * @author Zdream
 * @since 0.0.1
 * @date 2022-06-07
 */
public class FoePainter extends SpritePainter {
	public static final String CATEGORY_PAINTER = "painter";

	public final MFoe foe;
	public int z;
	public Function<MFoe, Boolean> flashChecker;

	public FoePainter(MFoe foe) {
		super(foe.description.painter.textures, foe.world.ticker);
		this.foe = foe;

		attachPainterBuffIfNeeded();
	}

	@Override
	public boolean getOrientation() {
		return foe.orientation;
	}

	@Override
	public float getBx() {
		return foe.anchorX;
	}

	@Override
	public float getBy() {
		return foe.anchorY;
	}

	@Override
	protected void checkEffects() {
		final boolean flash;
		if (flashChecker == null) {
			flash = foe.confirm(HealthModule.CATEGORY, TAG_IMMUNE);
		} else {
			flash = flashChecker.apply(foe);
		}

		if (flash) {
			applyFlash();
		} else {
			clearFlash();
		}

		if (foe.not(MFoe.TYPE_FOE) && foe.not(MFoe.TYPE_ELITE) && foe.not(MFoe.TYPE_LEADER)) {
			return;
		}

		final boolean hasDamage = foe.confirm(AttackModule.CATEGORY, TAG_HAS_DAMAGE);
		if (hasDamage) {
			applyRedStroke();
		} else {
			clearStroke();
		}
	}

	@Override
	public int zIndex() {
		return z;
	}

	/* **********
	 * 临时模块 *
	 ********** */

	public static final String PAINTER_HOOK_TYPE_STAGE = "stageUpdated";
	public static final String PAINTER_HOOK_TYPE_PATTERN = "patternUpdated";
	private FoeDescription.PainterHooks[] painterHooks;

	private void attachPainterBuffIfNeeded() {
		final FoeDescription description = foe.world.storage.findByFoeName(foe.foeName());
		if (description.painter == null || description.painter.hooks.length == 0) {
			return;
		}

		this.painterHooks = description.painter.hooks;
		PainterBuff buff = new PainterBuff(foe);
		foe.addBuff(buff);
	}

	private void onFoeUpdated(String type, String from, String to) {
		System.out.printf("FoePainter 接收到了状态转换: %s [%s->%s]%n", foe, from, to);

		final List<FoeDescription.PainterHooks> candidates = Arrays.stream(painterHooks)
				.filter(hook -> type.equals(hook.type))
				.filter(hook -> to.equals(hook.to))
				.collect(Collectors.toList());
		if (candidates.isEmpty()) {
			return;
		}
		if (candidates.size() == 1) {
			this.setState(candidates.get(0).setState);
		}

		// 如果有多个, 则 from 匹配的优先
		FoeDescription.PainterHooks candidate = candidates.stream()
				.filter(hook -> from.equals(hook.from))
				.findAny()
				.orElse(candidates.get(0));
		this.setState(candidate.setState);
	}

	class PainterBuff extends MFoeBuff {
		public PainterBuff(MFoe foe) {
			super(foe);
		}

		@Override
		public void init() {
			onHook(MHookType.UPDATE_PATTERN)
					.handle(this::onPatternUpdated)
					.withPriority(100)
					.buildAndSave();
			onHook(MHookType.UPDATE_STAGE)
					.handle(this::onStageUpdated)
					.withPriority(100)
					.buildAndSave();
		}

		private void onPatternUpdated(MHooks.MHookContext rawContext) {
			final MHooks.FoeStageUpdateHookContext context = rawContext.as();
			onFoeUpdated(PAINTER_HOOK_TYPE_PATTERN, context.lastPattern, context.currentPattern);
		}

		private void onStageUpdated(MHooks.MHookContext rawContext) {
			final MHooks.FoeStageUpdateHookContext context = rawContext.as();
			onFoeUpdated(PAINTER_HOOK_TYPE_STAGE, context.lastPattern, context.currentPattern);
		}

		@Override
		public String getCategory() {
			return CATEGORY_PAINTER;
		}
	}

	/* **********
	 *   工厂   *
	 ********** */

	public static SpritePainterFactory builderForWorld(MFoe foe) {
		return new SpritePainterFactory().setFoe(foe).useWorldBatch();
	}

	public static class SpritePainterFactory {
		int z;
		MFoe foe;
		Function<MFoe, Boolean> flashChecker;
		SpriteBatch batch;
		public SpritePainterFactory setZIndex(int z) {
			this.z = z;
			return this;
		}
		public SpritePainterFactory setFoe(MFoe foe) {
			this.foe = foe;
			return this;
		}
		public SpritePainterFactory flashWhenAgeBiggerThan(long threshold) {
			this.flashChecker = foe -> foe.age > threshold;
			return this;
		}
		public SpritePainterFactory useWorldBatch() {
			this.batch = RockChronicle.INSTANCE.graphics.batch;
			return this;
		}
		public SpritePainterFactory useUIBatch() {
			this.batch = RockChronicle.INSTANCE.graphics.uiBatch;
			return this;
		}
		public FoePainter create() {
			Objects.requireNonNull(batch, "你需要指定 SpriteBatch");
			FoePainter p = new FoePainter(
					Objects.requireNonNull(foe, "painter.foe 不能为 null"));
			p.batch = batch;
			p.z = z;
			p.flashChecker = flashChecker;
			return p;
		}
	}
}
